package com.android.rockchip.camera2;

import android.annotation.SuppressLint;
import android.media.AudioTrack;
import android.media.AudioManager;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.media.AudioDeviceInfo;
import android.util.Log;
import android.content.Context;
import android.os.SystemProperties;
import android.os.SystemClock;

import java.util.Timer;
import java.util.TimerTask;

public class AudioStream {
    private static final String TAG = "AudioStream_rkCamera2";
    private boolean isRecording = true;
    public boolean mIsStartup = false;
    private Thread record;
    private int mCurrOutput;
    private Context mContext;
	public boolean isAudioStart = false;
    private Timer timer;
    int count = 0;

    private static final String[] strOutDevice = {
            "bypass",                       // bypass to SPK in codec
            "hdmi",                         // output to hdmi
            "speaker",                      // output to SPK
            "usb",                          // output to usb audio
            "bluetooth",                    // output to bluetooth
            "hdmi,speaker,usb,bluetooth",   // output to all
            ""                              // auto select audio device
    };

    private static AudioStream mAudioStream;

    public static AudioStream getInstance(Context context){
        if(mAudioStream == null){
            mAudioStream = new AudioStream(context);
        }
        return mAudioStream;
    }

    private AudioStream(Context context) {
        mContext = context;
    }

    public void switchAudioOutput(int output) {
        Log.d(TAG, "switchAudioOutput to: " + strOutDevice[output]);

        if (output == mCurrOutput) {
            Log.d(TAG, "current output already is %d" + output);
            return;
        }
        // stop audio
        stop();
        // start audio
        start(output);
    }

    void start(int output) {
        Log.i(TAG, "start: " + output + ",strOutDevice: " + strOutDevice[output]);
        if (mIsStartup) {
            Log.w(TAG, "radio already startup");
            return;
        }
        mIsStartup = true;
        mCurrOutput = output;
//        if (mCurrOutput == 0) {
//            AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
//            audioManager.setParameters("HDMIin_enable=true");
//            return true;
//        }
        SystemProperties.set("media.audio.device_policy", strOutDevice[mCurrOutput]);
        Log.d(TAG, "setOutput: " + strOutDevice[mCurrOutput]);

        isRecording = true;
        record = new Thread(new recordSound());
        record.start();
    }

    void stop() {
        Log.d(TAG, "stop");
//        if (mCurrOutput == 0) {
//            AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
//            audioManager.setParameters("HDMIin_enable=false");
//        }
        isRecording = false;
        try {
            // wait thread finish
            if (record != null)
                record.join(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        mCurrOutput = 6;
        SystemProperties.set("media.audio.device_policy", strOutDevice[mCurrOutput]);
        Log.i(TAG, "setOutput: " + strOutDevice[mCurrOutput]);
    }

    class recordSound implements Runnable {
        AudioRecord m_in_rec;
        AudioTrack m_out_trk;

        public short[] toShortArray(byte[] src) {

            int count = src.length >> 1;
            short[] dest = new short[count];
            for (int i = 0; i < count; i++) {
                dest[i] = (short) (src[i * 2 + 1] << 8 | src[2 * i + 0] & 0xff);
            }
            return dest;
        }

        public byte[] toByteArray(short[] src) {

            int count = src.length;
            byte[] dest = new byte[count << 1];
            for (int i = 0; i < count; i++) {
                dest[i * 2 + 0] = (byte) (src[i] >> 0);
                dest[i * 2 + 1] = (byte) (src[i] >> 8);
            }

            return dest;
        }

        public void toByteArray(byte[] dest, short[] src) {

            int count = src.length;
            if (dest.length / 2 < count)
                count = dest.length / 2;
            for (int i = 0; i < count; i++) {
                dest[i * 2 + 0] = (byte) (src[i] >> 0);
                dest[i * 2 + 1] = (byte) (src[i] >> 8);
            }
        }

        private void rampVolume(byte[] inBytes, boolean up) {
            short[] inShorts = toShortArray(inBytes);
            int frameCount = inShorts.length / 2;
            Log.d(TAG, "ramp volume count: " + frameCount);
            float vl = up ? 0.0f : 1.0f;
            float vlInc = (up ? 1.0f : -1.0f) / frameCount;
            for (int i = 0; i < frameCount; i++) {
                float a = vl * (float) inShorts[i * 2];
                inShorts[i * 2] = (short) a;
                inShorts[i * 2 + 1] = (short) a;
                vl += vlInc;
            }

            toByteArray(inBytes, inShorts);
        }

        @SuppressLint("MissingPermission")
        public void run() {
            synchronized (this) {
                int frequence = 44100;
                int channelConfig = AudioFormat.CHANNEL_IN_STEREO;
                int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
                int m_out_buf_size = AudioTrack.getMinBufferSize(frequence,
                        channelConfig, audioEncoding);

                AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
                AudioDeviceInfo[] audioDevices = audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS);
                for (AudioDeviceInfo deviceInfo : audioDevices) {
                    Log.d(TAG, "AudioDeviceInfo getType " + deviceInfo.getType());
                }

                if (m_out_buf_size < 8192) {
                    Log.w(TAG, "Track buffer=" + m_out_buf_size + ", set to 8192");
                    m_out_buf_size = 8192;
                }
                m_out_trk = new AudioTrack(AudioManager.STREAM_MUSIC, frequence,
                        channelConfig,
                        audioEncoding, m_out_buf_size,
                        AudioTrack.MODE_STREAM);

                Log.d(TAG, "set media.audio.hdmiin 1");
                SystemProperties.set("media.audio.hdmiin", "1");
                SystemClock.sleep(100);
                byte[] m_in_bytes;
//                int m_in_buf_size = AudioRecord.getMinBufferSize(frequence, channelConfig, audioEncoding);
                int m_in_buf_size = 2048;
                Log.i(TAG, "out min: " + m_out_buf_size + ", in min: " + m_in_buf_size);
                m_in_rec = new AudioRecord(MediaRecorder.AudioSource.CAMCORDER, frequence, channelConfig,
                        AudioFormat.ENCODING_PCM_16BIT, m_in_buf_size);
                m_in_bytes = new byte[m_in_buf_size];
                m_in_rec.startRecording();
                m_out_trk.play();
                int readBytes = 0;
                if(timer != null){
                    timer.cancel();
                }
                timer = new Timer();
                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        if(count > 3){
                            isAudioStart = true;
                        }else{
                            isAudioStart = false;
                        }
                        count = 0;
                    }
                }, 500, 500);

                // ramp volume for begin
                rampVolume(m_in_bytes, true);
                while (isRecording) {
                    if ((readBytes > 0) && (m_out_trk != null)){
                        if(callBack != null){
                            byte[] tmp = new byte[readBytes];
                            System.arraycopy(m_in_bytes, 0, tmp, 0, readBytes);
                            callBack.onData(tmp);
                        }
                        m_out_trk.write(m_in_bytes, 0, readBytes);
                    }
                    readBytes = m_in_rec.read(m_in_bytes, 0, m_in_buf_size, AudioRecord.READ_NON_BLOCKING);
					if(readBytes != 0){
						count++;
					}
                }
            }
            Log.d(TAG, "exit hdmiin audio");
            m_in_rec.release();
            m_in_rec = null;
            Log.d(TAG, "set media.audio.hdmiin 0");
            SystemProperties.set("media.audio.hdmiin", "0");
            // ramp volume for end
            Log.d(TAG, "AudioTrack setVolume 0\n");
            m_out_trk.setVolume(0.0f);
            Log.d(TAG, "AudioTrack pause\n");
            m_out_trk.pause();
            SystemClock.sleep(50);
            Log.d(TAG, "AudioTrack stop\n");
            m_out_trk.release();
            m_out_trk = null;
            isAudioStart = false;
            if(timer != null){  //取消定时器，结束
                timer.cancel();
            }
            mIsStartup = false;
            if(callBack != null){
                callBack.onStop();
            }
        }
    }

    private AudioPCMDataCallBack callBack;

    public void setAudioPCMDataCallBack(AudioPCMDataCallBack callBack){
        this.callBack = callBack;
    }

    public interface AudioPCMDataCallBack{
        void onData(byte[] data);

        void onStop();
    }
}
